package cover

import (
	"CodeCover/internal/log"
	"bufio"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
)

func AppendTestFilesToCoverage(projectDir string, coverageFile string) error {
	module, err := GetModuleName(projectDir)
	if err != nil {
		return err
	}

	coverageFilePath := filepath.Join(projectDir, coverageFile)
	excludePaths := []string{coverageFilePath}

	targetFiles, err := findTargetFiles(projectDir, module, excludePaths)
	if err != nil {
		return err
	}

	existingEntries, err := parseCoverageFile(coverageFilePath)
	if err != nil {
		return err
	}
	fmt.Println("targetpath:", targetFiles)
	fmt.Println("existpath:", existingEntries)

	var newEntries []string
	for _, tf := range targetFiles {
		if !existingEntries[tf] {
			fmt.Println("needappend:", tf)
			newEntries = append(newEntries, fmt.Sprintf("%s:0.0,0.0 0 0", tf))
		}
	}

	if len(newEntries) == 0 {
		log.Logger.Info("No new files to append to coverage file")
		return nil
	}

	log.Logger.Info("Appending to coverage file:", newEntries)
	return appendToFile(coverageFilePath, newEntries)
}

func GetModuleName(projectDir string) (string, error) {
	goModPath := filepath.Join(projectDir, "go.mod")
	data, err := ioutil.ReadFile(goModPath)
	if err != nil {
		return "", fmt.Errorf("go.mod not found: %v", err)
	}

	scanner := bufio.NewScanner(strings.NewReader(string(data)))
	for scanner.Scan() {
		line := strings.TrimSpace(scanner.Text())
		if strings.HasPrefix(line, "module ") {
			parts := strings.Fields(line)
			if len(parts) < 2 {
				return "", fmt.Errorf("invalid module declaration in go.mod")
			}
			return parts[1], nil
		}
	}
	return "", fmt.Errorf("module declaration not found in go.mod")
}

func findTargetFiles(projectDir, module string, excludePaths []string) ([]string, error) {
	var targetFiles []string
	err := filepath.Walk(projectDir, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		if contains(excludePaths, path) {
			if info.IsDir() {
				return filepath.SkipDir
			}
			return nil
		}

		if info.IsDir() && isHidden(info.Name()) {
			return filepath.SkipDir
		}

		if !info.IsDir() {
			if isHidden(info.Name()) {
				return nil
			}
			relPath, err := filepath.Rel(projectDir, path)
			if err != nil {
				return err
			}
			unixPath := filepath.ToSlash(relPath)
			targetFiles = append(targetFiles, fmt.Sprintf("%s/%s", module, unixPath))
		}
		return nil
	})
	return targetFiles, err
}

func appendToFile(filename string, lines []string) error {
	original, err := ioutil.ReadFile(filename)
	if err != nil && !os.IsNotExist(err) {
		return err
	}

	var content strings.Builder
	content.Write(original)
	if len(original) > 0 && original[len(original)-1] != '\n' {
		content.WriteByte('\n')
	}
	content.WriteString(strings.Join(lines, "\n"))
	fmt.Println("writelines:", "\n"+strings.Join(lines, "\n"))
	return ioutil.WriteFile(filename, []byte(content.String()), 0644)
}

func isHidden(name string) bool {
	return strings.HasPrefix(name, ".")
}

func contains(slice []string, item string) bool {
	for _, s := range slice {
		if s == item {
			return true
		}
	}
	return false
}

func parseCoverageFile(path string) (map[string]bool, error) {
	existing := make(map[string]bool)

	content, err := ioutil.ReadFile(path)
	if os.IsNotExist(err) {
		return existing, nil
	} else if err != nil {
		return nil, err
	}

	scanner := bufio.NewScanner(strings.NewReader(string(content)))
	for scanner.Scan() {
		line := scanner.Text()
		parts := strings.Split(line, ":")
		line = parts[0]
		fmt.Println("line:", line)
		existing[line] = true // 记录完整行
	}

	return existing, nil
}
